import axios from "axios";

const before = async (config) => {
    config.api = "" + config.api;
    config.url = "" + config.url;
    config.method = ["POST", "GET"].includes(config.method) ? config.method : "GET";
    config.headers = Object.entries(new Object(config.headers)).reduce((headers, [key, value]) => {
        if (key.length > 0) headers[key] = value;
        return headers;
    }, {});
    config.body = Object.entries(new Object(config.body)).reduce((body, [key, value]) => {
        if (key.length > 0) body[key] = value;
        return body;
    }, {});
    config.query = Object.entries(new Object(config.query)).reduce((query, [key, value]) => {
        if (key.length > 0) query[key] = value;
        return query;
    }, {});
    config.timeout = config.timeout > 0 ? config.timeout : 0;
    config.abort = () => { };
    config.aborter = new axios.CancelToken((cancel) => config.abort = cancel);
    config.progress = config.progress instanceof Function ? config.progress : () => { };
    config.before = config.before instanceof Function ? config.before : () => { };
    config.after = config.after instanceof Function ? config.after : () => { };
    await config.before(config);
};

const handle = async (config) => {
    await axios.request({
        baseURL: config.api,
        url: config.url,
        method: config.method,
        headers: Object.entries(config.headers).reduce((headers, [key, value]) => {
            if (key.length === 0 || value === undefined || value === null) { }
            else if (typeof value === "string" || typeof value === "number") headers[key] = value;
            return headers;
        }, {}),
        data: Object.entries(config.body).reduce((body, [key, value]) => {
            if (key.length === 0 || value === undefined || value === null) { }
            else if (value instanceof Blob || typeof value === "string" || typeof value === "number") body.set(key, value);
            else if (value instanceof Object) body.set(key, JSON.stringify(value));
            return body;
        }, Object.values(config.body).some((value) => value instanceof Blob) ? new FormData() : new URLSearchParams()),
        params: Object.entries(config.query).reduce((query, [key, value]) => {
            if (key.length === 0 || value === undefined || value === null) { }
            else if (typeof value === "string" || typeof value === "number") query[key] = value;
            return query;
        }, {}),
        timeout: config.timeout,
        cancelToken: config.aborter,
        withCredentials: false,
        responseType: "json",
        responseEncoding: "utf8",
        onUploadProgress: config.progress,
        validateStatus: (status) => status > 0,
    }).then((res) => config.data = res).catch((error) => config.data = error);
};

const after = async (config) => {
    await config.after(config);
    let data = config.data;
    if (data instanceof Error) config.data = { code: data.name, msg: data.message };
    else config.data = { code: data.status, msg: data.statusText, data: data.data };
};

const xhr = window.xhr = {
    async post(url = "", body = {}, options = {}) {
        let config = { url, method: "POST", body, ...options };
        await before(config);
        await handle(config);
        await after(config);
        return config.data;
    },
    async get(url = "", query = {}, options = {}) {
        let config = { url, method: "GET", query, ...options };
        await before(config);
        await handle(config);
        await after(config);
        return config.data;
    },
};

export default xhr;